//三个状态

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (error) {
      this.reject(error)
    }
  }

  //初始化状态
  state = PENDING
  value = null
  reason = null
  onFulfilledCallbacks = []
  onRejectedCallbacks = []

  resolve = (value) => {
    if (this.state === PENDING) {
      this.state = FULFILLED
      this.value = value
      while (this.onFulfilledCallbacks.length) {
        this.onFulfilledCallbacks.shift()(value)
      }
    }
  }

  reject = (reason) => {
    if (this.state === PENDING) {
      this.state = REJECTED
      this.reason = reason
      while (this.onRejectedCallbacks.length) {
        this.onRejectedCallbacks.shift()(reason)
      }
    }
  }

  then = (onFulfilled, onRejected) => {
    //初始化传参
    const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
    const realOnRejected = typeof onRejected === 'function' ? onRejected : err => {
      throw err
    }

    const self = new MyPromise((resolve, reject) => {
      const fulfillMicrotask = () => {
        queueMicrotask(() => {
          try {
            resolvePromise(self, realOnFulfilled(this.value), resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      const rejectedMicrotask = () => {
        queueMicrotask(() => {
          try {
            resolvePromise(self, realOnRejected(this.reason), resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      }
      if (this.state === FULFILLED) fulfillMicrotask()
      if (this.state === REJECTED) rejectedMicrotask()
      if (this.state === PENDING) {
        this.onFulfilledCallbacks.push(fulfillMicrotask)
        this.onRejectedCallbacks.push(rejectedMicrotask)
      }
    })
    return self
  }

  static resolve(value) {
    if (value instanceof MyPromise) {
      return value
    } else {
      return new MyPromise(resolve => resolve(value))
    }
  }

  static reject(reason) {
    return new MyPromise((_, reject) => reject(reason))
  }
}


function resolvePromise(promise2, x, resolve, reject) {
  if (promise2 === x) return reject(new TypeError(''))

  if (typeof x === 'object' || typeof x === 'function') {
    if (x === null) return resolve(x)

    let then
    try {
      then = x.then
    } catch (error) {
      return reject(error)
    }

    if (typeof then === 'function') {
      let called = false
      try {
        then.call(x, y => {
          if (called) return
          called = true
          resolvePromise(x, y, resolve, reject)
        }, z => {
          if (called) return
          called = true
          reject(z)
        })
      } catch (error) {
        if (called) return
        reject(error)
      }
    } else {
      resolve(x)
    }
  } else {
    resolve(x)
  }
}


module.exports = MyPromise

MyPromise.deferred = function () {
  var result = {};
  result.promise = new MyPromise(function (resolve, reject) {
    result.resolve = resolve;
    result.reject = reject;
  });

  return result;
}

module.exports = MyPromise;